/*-----------------------------------------------------------------
 * 	CONSTANTS
 *-----------------------------------------------------------------*/

ul.Format = {};

/**
 * Defines constants keys for default formatting
 */
ul.Format.DEFAULT = "FORMATTER_DEFAULT";
/**
 * Defines constants keys for details formatting
 */
ul.Format.DETAILS = "FORMATTER_DETAILS";
/**
 * Defines constants keys for details formatting
 */
ul.Format.LOG = "FORMATTER_LOG";


/*-----------------------------------------------------------------
 * 	MANAGER DECLARATION
 *-----------------------------------------------------------------*/

/**
 * Manage the data formating process, it
 * catch new logs and return the corresponding
 * HTML code to write in the page body.
 */
ul.Format.FormatManager = function()
{
	/**
	 * The map into which formatters are registered
	 */
	var map = {};
	
	/**
	 * Registers the passed-in type 
	 *  
	 * @param {String} 		key
	 * @param {Formatter} 	formatter
	 */
	this.registerFormatter = function ( key, formatter )
	{
		formatter.setOwner ( this );
		map[ key ] = formatter;
	};
	/**
	 * 
	 * @param {String} key
	 */
	this.unregisterFormatter = function ( key )
	{
		map[ key ].setOwner( null );
		delete map[ key ];
	};
	/**
	 * 
	 * @param {String} key
	 * @return 
	 */
	this.containsKey = function( key )
	{
		return map[key] !== undefined;
	};
	
	/**
	 * 
	 * @param {Object} key
	 * @return 
	 */
	this.getFormatter = function( key )
	{
		return map[ key ];
	};
	
	/**
	 * 
	 * @param {Object} content
	 * @return
	 */
	this.getType = function ( content )
	{
		if( this.containsKey( content.type ) )
			return content.type;
		else if( this.containsKey( content.atype ) )
			return content.atype;
		else 
			return ul.Format.DEFAULT;
	};
	
	// initialization
	this.registerFormatter( ul.Format.LOG, new ul.Format.LogFormatter() );	
	this.registerFormatter( ul.Format.DEFAULT, new ul.Format.DefaultFormatter() );
	this.registerFormatter( ul.Format.DETAILS, new ul.Format.DetailsFormatter() );
	this.registerFormatter( "String", new ul.Format.StringFormatter() );
	this.registerFormatter( "Object", new ul.Format.ObjectFormatter() );
	this.registerFormatter( "XML", new ul.Format.XMLFormatter() );
	this.registerFormatter( "Array", new ul.Format.ArrayFormatter() );
	this.registerFormatter( "Source", new ul.Format.SourceFormatter() );
	
};

/*-----------------------------------------------------------------
 * 	FORMATTERS DECLARATION
 *-----------------------------------------------------------------*/

/**
 * Abstract class for all formatters, the abstract class provides
 * convenient methods to handle owner of the formatters.
 */
ul.Format.AbstractFormatter = function (){}
ul.Format.AbstractFormatter.prototype = {
	
	/**
	 * A reference to the formatter manager which own this formatter
	 */
	owner:null,
	
	/**
	 * Defines the owner of this formatter.
	 * @param {FormatterManager} owner manager which own this formatter
	 */
	setOwner: function ( owner )
	{
		this.owner = owner;	
	},
	
	/**
	 * Returns the owner of this formatter.
	 * 
	 * @return the owner of this formatter
	 */
	getOwner: function ()
	{
		return this.owner;
	}	
};

/*-----------------------------------------------------------------
 * 	CONCRET FORMATTERS DECLARATION
 *-----------------------------------------------------------------*/

/**
 * Format the whole log to display, aggregate
 * the formatter for log's details and log's
 * content.
 */
ul.Format.LogFormatter = function()
{
	this.format = function ( data, type )
	{
		var level = data.details.level;
		var type = this.getOwner().getType ( data.message );
		
		var details = this.getOwner().getFormatter( ul.Format.DETAILS ).format( data.details, data.message.type ); 
		var message = this.getOwner().getFormatter( type ).format( data.message, data.message.type ); 
		
		return ul.Templates.parse( ul.Templates.LOG, level, message, details, IdFactory.getNextId() );
	};
};
// Defines inheritance
ul.Format.LogFormatter.prototype = ul.Format.AbstractFormatter.prototype;

/**
 * Format data when the message type isn't registered
 */
ul.Format.DefaultFormatter = function()
{
	this.format = function ( data, type )
	{
		return data.value ? data.value.toString ().escapeHTML(
									).replace( /((#|0x)+([0-9a-fA-F]{6,8}))/g, 
		 									ul.Templates.COLOR
								    ).replace( /(http:\/\/[a-zA-Z0-9\/_.-]*)/g, 
											ul.Templates.URL
								    ).replace( /([a-zA-Z0-9\/_.-]+@[a-zA-Z0-9\/_.-]+)/, 
											ul.Templates.MAIL  
								    ) : data.value;
	};
};
// Defines inheritance
ul.Format.DefaultFormatter.prototype = new ul.Format.AbstractFormatter();

/**
 * Format as a string (with double-quotes) when the data
 * is in a property or an entry of the message
 */
ul.Format.StringFormatter = function()
{
	this.format = function ( data, type )
	{
		return ul.Templates.parse( ul.Templates.STRING, ( this.getOwner().getFormatter( ul.Format.DEFAULT ).format( data, data.type ) ) );
	};
};
// Defines inheritance
ul.Format.StringFormatter.prototype = new ul.Format.AbstractFormatter();

/**
 * Format as a XML string
 */
ul.Format.XMLFormatter = function()
{
	this.format = function ( data, type )
	{
		var content = data.value.toString().escapeHTML();
		
		return ul.Templates.parse( ul.Templates.XML, type, content, IdFactory.getNextId() );
	};
};
// Defines inheritance
ul.Format.XMLFormatter.prototype = new ul.Format.AbstractFormatter();

/**
 * Format a string as a source code of the specified language,
 * language is defined as prefix of the source code such : 
 * javascript:function(){};
 */
ul.Format.SourceFormatter = function()
{
	this.format = function ( data, type )
	{
		var value = data.value .toString(); 
		var id = value.indexOf ( ":" );
		var language = value.substr( 0, id );
		var name = language.capitalize();
		var source = value.substr( id + 1 ).escapeHTML();
		
		return ul.Templates.parse( ul.Templates.SOURCE, name, language, source, IdFactory.getNextId() );
	};
};
// Defines inheritance
ul.Format.SourceFormatter.prototype = new ul.Format.AbstractFormatter();

/**
 * Format the object, and recursively format its
 * properties. 
 */
ul.Format.ObjectFormatter = function()
{
	this.format = function ( data, type )
	{
		var properties = "";
		
		for( var i in data.value )
		{
			var o = data.value[i];
			var t = this.getOwner().getType( o );
			var property = this.getOwner().getFormatter( t ).format( o, o.type );
			
			properties += ul.Templates.parse( ul.Templates.PROPERTY, i, property );
		}
		return  ul.Templates.parse( ul.Templates.OBJECT, type, properties, IdFactory.getNextId() );
	};
};
// Defines inheritance
ul.Format.ObjectFormatter.prototype = new ul.Format.AbstractFormatter();


/**
 * Format the array, and recursively format its
 * entries. 
 */
ul.Format.ArrayFormatter = function()
{
	this.format = function ( data, type )
	{
		var entries = "";
		var l = data.value.length;
		for( var i = 0; i < l; i++ )
		{
			var o = data.value[i];
			var t = this.getOwner().getType( o );
			var entry = this.getOwner().getFormatter( t ).format( o, o.type );
			
			entries += ul.Templates.parse( ul.Templates.ENTRY, i % 2 ? "even" : "odd", i, entry );
		}
		return  ul.Templates.parse( ul.Templates.ARRAY, type, entries, IdFactory.getNextId() );
	};
};
// Defines inheritance
ul.Format.ArrayFormatter.prototype = new ul.Format.AbstractFormatter();

/**
 * Format the details of the log
 */
ul.Format.DetailsFormatter = function()
{
	this.format = function ( data, type )
	{
		var level = data.level;
		var time = data.time;		
		var file = data.file;
		var clazz = data.clazz;
		var func = data.func;
		var line = data.line;
		var opt = "";
		
		if( clazz  )
			opt = ul.Templates.parse( ul.Templates.OPTIONNAL_DETAILS, clazz + "." , func, line, file );
		
		return ul.Templates.parse( ul.Templates.DETAILS, level, time, opt );
		
	};
};
// Defines inheritance
ul.Format.DetailsFormatter.prototype = new ul.Format.AbstractFormatter();